inspector: Deal with dying objects
authorMatthias Clasen <mclasen@redhat.com>
Fri, 9 May 2014 02:58:43 +0000 (22:58 -0400)
committerMatthias Clasen <mclasen@redhat.com>
Sun, 11 May 2014 02:04:28 +0000 (22:04 -0400)
The widget-tree was not safe against object just going away.
Fix this by using row references instead of iters where
necessary, and by using weak refs to clean up when objects
die.

modules/inspector/classes-list.c
modules/inspector/classes-list.ui
modules/inspector/css-editor.c
modules/inspector/css-editor.h
modules/inspector/prop-list.c
modules/inspector/widget-tree.c

index 7c0d52a7444da7484c5e05698dd8e5aa4a97ea5b..cc5efbcff265340c4e7e2a790952f5abbce00081 100644 (file)
@@ -39,17 +39,27 @@ typedef struct
 
 struct _GtkInspectorClassesListPrivate
 {
-  GtkWidget *toolbar;
-  GtkWidget *view;
-  GtkTreeViewColumn *column;
-  GtkCellRenderer *name_renderer;
   GtkListStore *model;
-  GHashTable *contexts;
-  GtkStyleContext *current_context;
+  GtkStyleContext *context;
 };
 
 G_DEFINE_TYPE_WITH_PRIVATE (GtkInspectorClassesList, gtk_inspector_classes_list, GTK_TYPE_BOX)
 
+static void
+set_hash_context (GtkInspectorClassesList *cl, GHashTable *hash_context)
+{
+  g_object_set_data_full (G_OBJECT (cl->priv->context),
+                                    "gtk-inspector-hash-context",
+                                    hash_context, (GDestroyNotify)g_hash_table_unref);
+}
+
+static GHashTable *
+get_hash_context (GtkInspectorClassesList *cl)
+{
+  return (GHashTable *)g_object_get_data (G_OBJECT (cl->priv->context),
+                                          "gtk-inspector-hash-context");
+}
+
 static void
 enabled_toggled (GtkCellRendererToggle   *renderer,
                  const gchar             *path,
@@ -76,7 +86,7 @@ enabled_toggled (GtkCellRendererToggle   *renderer,
                       COLUMN_ENABLED, enabled,
                       -1);
 
-  context = g_hash_table_lookup (cl->priv->contexts, cl->priv->current_context);
+  context = get_hash_context (cl);
   if (context)
     {
       c = g_hash_table_lookup (context, name);
@@ -84,9 +94,9 @@ enabled_toggled (GtkCellRendererToggle   *renderer,
         {
           c->enabled = enabled;
           if (enabled)
-            gtk_style_context_add_class (cl->priv->current_context, name);
+            gtk_style_context_add_class (cl->priv->context, name);
           else
-            gtk_style_context_remove_class (cl->priv->current_context, name);
+            gtk_style_context_remove_class (cl->priv->context, name);
         }
       else
         g_warning ("GtkInspector: Couldn't find the css class %s in the class hash table.", name);
@@ -120,14 +130,16 @@ add_clicked (GtkButton               *button,
 
   if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK)
     {
-      const gchar *name = gtk_entry_get_text (GTK_ENTRY (entry));
-      GHashTable *context = g_hash_table_lookup (cl->priv->contexts, cl->priv->current_context);
+      const gchar *name;
+      GHashTable *context;
 
+      context = get_hash_context (cl);
+      name = gtk_entry_get_text (GTK_ENTRY (entry));
       if (*name && !g_hash_table_contains (context, name))
         {
           GtkTreeIter tree_iter;
 
-          gtk_style_context_add_class (cl->priv->current_context, name);
+          gtk_style_context_add_class (cl->priv->context, name);
 
           GtkInspectorClassesListByContext *c = g_new0 (GtkInspectorClassesListByContext, 1);
           c->enabled = TRUE;
@@ -155,7 +167,7 @@ read_classes_from_style_context (GtkInspectorClassesList *cl)
   GHashTable *hash_context;
 
   hash_context = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
-  classes = gtk_style_context_list_classes (cl->priv->current_context);
+  classes = gtk_style_context_list_classes (cl->priv->context);
 
   for (l = classes; l; l = l->next)
     {
@@ -171,7 +183,7 @@ read_classes_from_style_context (GtkInspectorClassesList *cl)
                           -1);
     }
     g_list_free (classes);
-    g_hash_table_replace (cl->priv->contexts, cl->priv->current_context, hash_context);
+    set_hash_context (cl, hash_context);
 }
 
 static void
@@ -181,15 +193,16 @@ restore_defaults_clicked (GtkButton           *button,
   GHashTableIter hash_iter;
   gchar *name;
   GtkInspectorClassesListByContext *c;
-  GHashTable *hash_context = g_hash_table_lookup (cl->priv->contexts, cl->priv->current_context);
+  GHashTable *hash_context;
 
+  hash_context = get_hash_context (cl);
   g_hash_table_iter_init (&hash_iter, hash_context);
   while (g_hash_table_iter_next (&hash_iter, (gpointer *)&name, (gpointer *)&c))
     {
       if (c->style == PANGO_STYLE_ITALIC)
-        gtk_style_context_remove_class (cl->priv->current_context, name);
+        gtk_style_context_remove_class (cl->priv->context, name);
       else if (!c->enabled)
-        gtk_style_context_add_class (cl->priv->current_context, name);
+        gtk_style_context_add_class (cl->priv->context, name);
     }
 
   gtk_list_store_clear (cl->priv->model);
@@ -201,27 +214,47 @@ gtk_inspector_classes_list_init (GtkInspectorClassesList *cl)
 {
   cl->priv = gtk_inspector_classes_list_get_instance_private (cl);
   gtk_widget_init_template (GTK_WIDGET (cl));
-  cl->priv->contexts = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)g_hash_table_destroy);
+}
+
+static void remove_dead_object (gpointer data, GObject *dead_object);
+
+static void
+cleanup_context (GtkInspectorClassesList *cl)
+{
+  if (cl->priv->context)
+    g_object_weak_unref (G_OBJECT (cl->priv->context), remove_dead_object, cl);
+
+  gtk_list_store_clear (cl->priv->model);
+  cl->priv->context = NULL;
+  gtk_widget_set_sensitive (GTK_WIDGET (cl), FALSE);
+}
+
+static void
+remove_dead_object (gpointer data, GObject *dead_object)
+{
+  GtkInspectorClassesList *cl = data;
+
+  cl->priv->context = NULL;
+  cleanup_context (cl);
 }
 
 void
 gtk_inspector_classes_list_set_widget (GtkInspectorClassesList *cl,
                                        GtkWidget               *widget)
 {
-  GtkStyleContext *widget_context;
   GHashTable *hash_context;
   GtkTreeIter tree_iter;
   GtkInspectorClassesListByContext *c;
 
-  gtk_list_store_clear (cl->priv->model);
+  cleanup_context (cl);
 
   gtk_widget_set_sensitive (GTK_WIDGET (cl), TRUE);
-  widget_context = gtk_widget_get_style_context (widget);
 
-  cl->priv->current_context = widget_context;
-  gtk_widget_set_sensitive (cl->priv->toolbar, TRUE);
+  cl->priv->context = gtk_widget_get_style_context (widget);
 
-  hash_context = g_hash_table_lookup (cl->priv->contexts, widget_context);
+  g_object_weak_ref (G_OBJECT (cl->priv->context), remove_dead_object, cl);
+
+  hash_context = get_hash_context (cl);
   if (hash_context)
     {
       GHashTableIter hash_iter;
@@ -239,9 +272,7 @@ gtk_inspector_classes_list_set_widget (GtkInspectorClassesList *cl,
         }
     }
   else
-    {
-      read_classes_from_style_context (cl);
-    }
+    read_classes_from_style_context (cl);
 }
 
 static void
@@ -250,11 +281,7 @@ gtk_inspector_classes_list_class_init (GtkInspectorClassesListClass *klass)
   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
 
   gtk_widget_class_set_template_from_resource (widget_class, "/org/gtk/inspector/classes-list.ui");
-  gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorClassesList, toolbar);
-  gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorClassesList, view);
   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorClassesList, model);
-  gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorClassesList, column);
-  gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorClassesList, name_renderer);
   gtk_widget_class_bind_template_callback (widget_class, add_clicked);
   gtk_widget_class_bind_template_callback (widget_class, restore_defaults_clicked);
   gtk_widget_class_bind_template_callback (widget_class, enabled_toggled);
index e26700ff3f6fd024de3f3e0405c9f7d43e76656e..0835eefe6162baabe7996a89993cd228c07bcde3 100644 (file)
@@ -10,7 +10,7 @@
   <template class="GtkInspectorClassesList" parent="GtkBox">
     <property name="orientation">vertical</property>
     <child>
-      <object class="GtkToolbar" id="toolbar">
+      <object class="GtkToolbar">
         <property name="visible">True</property>
         <property name="icon-size">small-toolbar</property>
         <property name="sensitive">False</property>
         <property name="visible">True</property>
         <property name="expand">True</property>
         <child>
-          <object class="GtkTreeView" id="view">
+          <object class="GtkTreeView">
             <property name="visible">True</property>
             <property name="model">model</property>
             <child>
-              <object class="GtkTreeViewColumn" id="column">
+              <object class="GtkTreeViewColumn">
                 <property name="title" translatable="yes">Name</property>
                 <child>
                   <object class="GtkCellRendererToggle">
@@ -53,7 +53,7 @@
                   </attributes>
                 </child>
                 <child>
-                  <object class="GtkCellRendererText" id="name_renderer">
+                  <object class="GtkCellRendererText">
                     <property name="scale">0.8</property>
                   </object>
                   <attributes>
index b993e24fa3fca540cf3e52bc5cc46ad38c13b0ee..d5a59b9fa87a242b380c199f237a6b0ab9406b34 100644 (file)
@@ -205,8 +205,9 @@ create_provider (GtkInspectorCssEditor *ce)
       gtk_style_context_add_provider (ce->priv->context,
                                       GTK_STYLE_PROVIDER (provider),
                                       G_MAXUINT);
-      g_object_set_data (G_OBJECT (ce->priv->context),
-                         GTK_INSPECTOR_CSS_EDITOR_PROVIDER, provider);
+      g_object_set_data_full (G_OBJECT (ce->priv->context),
+                              GTK_INSPECTOR_CSS_EDITOR_PROVIDER, provider,
+                              g_object_unref);
     }
 
   g_signal_connect (provider, "parsing-error",
@@ -302,6 +303,15 @@ gtk_inspector_css_editor_new (gboolean global)
                                    NULL));
 }
 
+static void
+remove_dead_object (gpointer data, GObject *dead_object)
+{
+  GtkInspectorCssEditor *ce = data;
+
+  ce->priv->context = NULL;
+  gtk_widget_set_sensitive (GTK_WIDGET (ce), ce->priv->global);
+}
+
 void
 gtk_inspector_css_editor_set_widget (GtkInspectorCssEditor *ce,
                                      GtkWidget             *widget)
@@ -316,6 +326,7 @@ gtk_inspector_css_editor_set_widget (GtkInspectorCssEditor *ce,
 
   if (ce->priv->context)
     {
+      g_object_weak_unref (G_OBJECT (ce->priv->context), remove_dead_object, ce);
       text = get_current_text (GTK_TEXT_BUFFER (ce->priv->text));
       g_object_set_data_full (G_OBJECT (ce->priv->context),
                               GTK_INSPECTOR_CSS_EDITOR_TEXT,
@@ -331,6 +342,8 @@ gtk_inspector_css_editor_set_widget (GtkInspectorCssEditor *ce,
 
   set_initial_text (ce);
   disable_toggled (ce->priv->disable_button, ce);
+
+  g_object_weak_ref (G_OBJECT (ce->priv->context), remove_dead_object, ce);
 }
 
 // vim: set et sw=2 ts=2:
index 80be3099ddc2e8e65bf515436154ec24e04dcb93..08ee4e0526113509526e66338bf8438279b63a27 100644 (file)
@@ -50,7 +50,7 @@ G_BEGIN_DECLS
 
 GType      gtk_inspector_css_editor_get_type   (void);
 GtkWidget *gtk_inspector_css_editor_new        (gboolean               global);
-void       gtk_inspector_css_editor_set_widget (GtkInspectorCssEditor *editor,
+void       gtk_inspector_css_editor_set_widget (GtkInspectorCssEditor *ce,
                                                 GtkWidget             *widget);
 
 G_END_DECLS
index d58cbc3aea181934da1dc8c60304f4e0f1618741..6c78777ddc4489678f96220a99bf1f6a33aff447 100644 (file)
@@ -232,6 +232,38 @@ gtk_inspector_prop_list_new (GtkWidget *widget_tree,
                        NULL);
 }
 
+static void remove_dead_object (gpointer data, GObject *dead_object);
+
+static void
+cleanup_object (GtkInspectorPropList *pl)
+{
+  gtk_widget_set_sensitive (GTK_WIDGET (pl), FALSE);
+
+  if (pl->priv->object)
+    g_object_weak_unref (pl->priv->object, remove_dead_object, pl);
+
+  if (pl->priv->object && pl->priv->notify_handler_id != 0)
+    {
+      g_signal_handler_disconnect (pl->priv->object, pl->priv->notify_handler_id);
+      pl->priv->notify_handler_id = 0;
+    }
+
+  pl->priv->object = NULL;
+
+  g_hash_table_remove_all (pl->priv->prop_iters);
+  gtk_list_store_clear (pl->priv->model);
+}
+
+static void
+remove_dead_object (gpointer data, GObject *dead_object)
+{
+  GtkInspectorPropList *pl = data;
+
+  pl->priv->notify_handler_id = 0;
+  pl->priv->object = NULL;
+  cleanup_object (pl);
+}
+
 gboolean
 gtk_inspector_prop_list_set_object (GtkInspectorPropList *pl,
                                     GObject              *object)
@@ -244,18 +276,16 @@ gtk_inspector_prop_list_set_object (GtkInspectorPropList *pl,
   if (pl->priv->object == object)
     return FALSE;
 
-  if (pl->priv->notify_handler_id != 0)
-    {
-      g_signal_handler_disconnect (pl->priv->object, pl->priv->notify_handler_id);
-      pl->priv->notify_handler_id = 0;
-    }
-
-  g_hash_table_remove_all (pl->priv->prop_iters);
-  gtk_list_store_clear (pl->priv->model);
-  gtk_widget_set_sensitive (GTK_WIDGET (pl), FALSE);
+  cleanup_object (pl);
 
   pl->priv->object = object;
 
+  g_object_weak_ref (object, remove_dead_object, pl);
+
+  g_object_set (pl->priv->attribute_column,
+                "visible", !pl->priv->child_properties && GTK_IS_CELL_RENDERER (object),
+                NULL);
+
   if (pl->priv->child_properties)
     {
       GtkWidget *parent;
@@ -294,9 +324,6 @@ gtk_inspector_prop_list_set_object (GtkInspectorPropList *pl,
                         G_CALLBACK (gtk_inspector_prop_list_prop_changed_cb),
                         pl);
 
-  g_object_set (pl->priv->attribute_column,
-                "visible", !pl->priv->child_properties && GTK_IS_CELL_RENDERER (object),
-                NULL);
   return TRUE;
 }
 
index 165339a2a287d7bf22e34beba000fc24c19da638..4b63cb838ab6948edf75a65a3e98cf0cde825942 100644 (file)
@@ -63,20 +63,42 @@ on_widget_selected (GtkTreeSelection       *selection,
 
 typedef struct
 {
+  GtkInspectorWidgetTree *wt;
   GObject *object;
-  GtkTreeIter *iter;
+  GtkTreeRowReference *row;
   gulong map_handler;
   gulong unmap_handler;
 } ObjectData;
 
+static void
+remove_dead_object (gpointer data, GObject *dead_object)
+{
+  ObjectData *od = data;
+
+  if (gtk_tree_row_reference_valid (od->row))
+    {
+      GtkTreePath *path;
+      GtkTreeIter iter;
+      path = gtk_tree_row_reference_get_path (od->row);
+      gtk_tree_model_get_iter (GTK_TREE_MODEL (od->wt->priv->model), &iter, path);
+      gtk_tree_store_remove (od->wt->priv->model, &iter);
+      gtk_tree_path_free (path);
+    }
+  od->object = NULL;
+  g_hash_table_remove (od->wt->priv->iters, dead_object);
+}
+
 static void
 object_data_free (gpointer data)
 {
   ObjectData *od = data;
 
-  gtk_tree_iter_free (od->iter);
+  gtk_tree_row_reference_free (od->row);
 
-  if (g_signal_handler_is_connected (od->object, od->map_handler))
+  if (od->object)
+    g_object_weak_unref (od->object, remove_dead_object, od);
+
+  if (od->object && od->map_handler)
     {
       g_signal_handler_disconnect (od->object, od->map_handler);
       g_signal_handler_disconnect (od->object, od->unmap_handler);
@@ -181,6 +203,7 @@ gtk_inspector_widget_tree_append_object (GtkInspectorWidgetTree *wt,
                                          const gchar            *name)
 {
   GtkTreeIter iter;
+  GtkTreePath *path;
   const gchar *class_name = G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (object));
   gchar *address;
   gboolean mapped;
@@ -231,8 +254,11 @@ gtk_inspector_widget_tree_append_object (GtkInspectorWidgetTree *wt,
                       -1);
 
   od = g_new0 (ObjectData, 1);
+  od->wt = wt;
   od->object = object;
-  od->iter = gtk_tree_iter_copy (&iter);
+  path = gtk_tree_model_get_path (GTK_TREE_MODEL (wt->priv->model), &iter);
+  od->row = gtk_tree_row_reference_new (GTK_TREE_MODEL (wt->priv->model), path);
+  gtk_tree_path_free (path);
   if (GTK_IS_WIDGET (object))
     {
       od->map_handler = g_signal_connect (object, "map", G_CALLBACK (map_or_unmap), wt);
@@ -240,6 +266,7 @@ gtk_inspector_widget_tree_append_object (GtkInspectorWidgetTree *wt,
     }
 
   g_hash_table_insert (wt->priv->iters, object, od);
+  g_object_weak_ref (object, remove_dead_object, od);
 
   g_free (address);
 
@@ -306,9 +333,14 @@ gtk_inspector_widget_tree_find_object (GtkInspectorWidgetTree *wt,
   ObjectData *od;
 
   od = g_hash_table_lookup (wt->priv->iters, object);
-  if (od)
+  if (od && gtk_tree_row_reference_valid (od->row))
     {
-      *iter = *od->iter;
+      GtkTreePath *path;
+
+      path = gtk_tree_row_reference_get_path (od->row);
+      gtk_tree_model_get_iter (GTK_TREE_MODEL (wt->priv->model), iter, path);
+      gtk_tree_path_free (path);
+
       return TRUE;
     }